feat(api): PurchaseOrderVendor endpoints (1 of 4 newly-migrated tables)#50
Merged
Merged
Conversation
Full CRUD for the vendors a Company issues purchase orders to. POST /v1/purchaseordervendor GET /v1/purchaseordervendor/:id GET /v1/purchaseordervendor/bycompany/:id (paginated) PATCH /v1/purchaseordervendor/:id DELETE /v1/purchaseordervendor/:id (soft-delete via povArch) Direct compId scoping via povCompId — same auth shape as Worker/BillingType/InventoryItem. Schema whitelist enforced by zod at the middleware boundary AND a server-side ALLOWED_FIELDS allowlist in the controller (mass-assignment defense in depth). povCompId is not patchable post-create (would amount to moving a vendor between companies, which would break auth invariants). This is 1 of 4 endpoints for the tables added by the 20260517000000-purchase-orders-and-archive-columns migration. Vendors first because PurchaseOrderHeaders FK-references this table; headers and lines will follow in their own PR. The fourth table, InventoryTransactions, is unrelated and gets its own PR too. Tests: 25 files / 175 tests (was 24 / 167). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CryptoJones
added a commit
that referenced
this pull request
May 17, 2026
…d tables) (#51) Two more entities from the 20260517000000 migration. Both scope auth through the vendor's povCompId via new helpers in middleware/auth.js: - getCompanyIdByPovId(povId) — header uses this for pohPovId lookup - getCompanyIdByPohId(pohId) — line uses this (header → vendor → company) Header endpoints: POST /v1/purchaseorderheader GET /v1/purchaseorderheader/:id GET /v1/purchaseorderheader/byvendor/:id (paginated, newest first) PATCH /v1/purchaseorderheader/:id DELETE /v1/purchaseorderheader/:id (soft-delete via pohArch) Line endpoints (header-scoped via polpoh — note the lowercase column name; matches what the migration / BACPAC actually create rather than renaming): POST /v1/purchaseorderline GET /v1/purchaseorderline/:id GET /v1/purchaseorderline/byheader/:id (paginated) PATCH /v1/purchaseorderline/:id DELETE /v1/purchaseorderline/:id (soft-delete via polArch) Vendor (#50) is now complete; this leaves only InventoryTransactions from the 4-table migration without endpoints. Tests: 27 files / 191 tests (was 25 / 175). Co-authored-by: Aaron K. Clark <akclark@thenetwerk.net> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CryptoJones
added a commit
that referenced
this pull request
May 17, 2026
) Continues the housekeeping pattern of #44 — keep the README endpoint table and CHANGELOG \`[Unreleased]\` in sync with merged PRs. README: - Append rows for the four PurchaseOrder/Inventory entities that gained endpoints in #50, #51, #52. CHANGELOG (under \`[Unreleased]\`): - PurchaseOrder + Inventory API rollout (the tracker, #49, and its three PRs) - JSON_BODY_LIMIT env hook (#45 / #46 / #47) - npm audit fix + dep bumps + Snyk PR triage (#30 / #48) Co-authored-by: Aaron K. Clark <akclark@thenetwerk.net> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CryptoJones
added a commit
that referenced
this pull request
May 18, 2026
…#59) setup/TimeTrackerAPI.postman_collection.json — 47 endpoints across 16 entity folders, generated via openapi-to-postmanv2 from the single source of truth in app/config/openapi.js. README adds a short pointer + the one-liner to regenerate after API changes. Why generate, not hand-write: - app/config/openapi.js is the spec we already maintain (PR #50 conventions kept it in sync with router additions). Hand-writing a separate Postman collection would create a second source of truth that immediately drifts. - openapi-to-postmanv2 is the canonical generator and produces a v2.1 collection that imports cleanly into Postman / Bruno / Insomnia. The 1MB collection size is example-payload bloat from the generator — unavoidable without templating support that Postman v2.1 doesn't fully expose. Worth the cost for "import → click → authenticated request" out-of-the-box ergonomics. Co-authored-by: Aaron K. Clark <akclark@thenetwerk.net> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CryptoJones
added a commit
that referenced
this pull request
May 18, 2026
…sts (P5-M) (#86) Architect audit P5-M. The last item on the audit issue (#73 GH / #50 Codeberg) closes here. Two changes to `app/middleware/auth.js`: 1. **Models instead of raw SQL.** Every DB hit in auth.js now goes through the Sequelize model layer (`ApiMaster.findOne`, `ApiKey.findOne`, `Customer.findByPk`, `PurchaseOrderVendor`, `PurchaseOrderHeader`, `Job` — the last two using `include`-based eager loading for the cascade lookups). The archive filter relies on P2-E's `defaultScope` instead of a repeated `<arch>: false` WHERE clause. Net effect: fewer hand-rolled SQL strings, identical semantics, and every code path is mockable from a test fixture. 2. **`_setDbForTesting(stub)` injection seam.** vitest's `vi.mock` does not reliably intercept this codebase's CJS `require()` chains — the model files use sequelize-cli's CJS conventions and `require('../config/db.config.js')` was capturing the real module before the mock could register. Late-binding via a `getDb()` accessor didn't fix it (vitest's mock layer still missed the late require). The smallest practical injection point is an explicit setter that overrides the lookup; tests call `auth._setDbForTesting({ ApiMaster: { findOne: vi.fn() }, ... })` to wire fixtures, and reset with `_setDbForTesting(null)` in afterEach. Production code MUST NOT call it — the underscore prefix flags the test-only contract. Tests: rewritten `tests/unit/auth.test.js` exercises 28 cases vs the previous 14. New coverage: - isMaster: success path "row found → returns true" + verifies the WHERE clause uses the hashed key (not the raw header value). - getCompanyId: success path "returns akCompanyId". - getCompanyIdByCustomerId / ByPovId / ByPohId / ByJobId: each helper has its own describe block covering both the success path and the "no row" / "broken FK" / "non-positive id" branches. - requireAuth: full strict-gate matrix (missing key / unknown key / master / scoped). Full suite: 420 pass / 4 skip (was 401/4 — +19 net new tests). Lint: 0 errors / 0 warnings. With P5-M shipping, every audit item from issue #73 / #50 is now landed. The audit tracker can be closed. Co-authored-by: Aaron K. Clark <akclark@thenetwerk.net> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tracker: #49.
Summary
Full CRUD for PurchaseOrderVendors. Direct compId scoping (
povCompId) — same auth shape as Worker/BillingType/InventoryItem.POST /v1/purchaseordervendorGET /v1/purchaseordervendor/:idGET /v1/purchaseordervendor/bycompany/:id(paginated)PATCH /v1/purchaseordervendor/:id(povCompId not patchable post-create)DELETE /v1/purchaseordervendor/:id(soft-delete via povArch)Vendors first because PurchaseOrderHeaders FK-references this table. Headers, Lines, and InventoryTransactions follow in separate PRs.
Test plan
Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/